Using Arquillian to replace Embedded Container
When deployed on AS4 RHQ used the JBoss Embedded Container to perform unit/integration tests on domain (JPA peristence) and server jar (SLSB) classes. Now that we deploy on AS7, the embedded container is no longer an option.
The de facto alternative is Arquillian. Arquillian is a tool that integrates multiple test frameworks, namely JUnit and TestNG, against several containers, namely Glassfish, OpenEJB and, most importantly for us, AS7.
It basically works by starting the desired container, deploying a test deployment (EAR, WAR, EJB JAR) that contains the objects under test, as well as the test code, and then interacting with it via its own WAR file, which it deploys and that manages the testing.
For us this meant taking our legacy AbstractEJB3Test classes (one for domain, one for server jar) and migrating them from using embedded container to using Arquillian. The AbstractEJB3Test classes remain, keeping the resource hierarchy intact, but the implementations are completely different.
For domain we maintained the same module structure under src/test. But now AbstractEJB3Test assembles a test EAR and makes that the Arquillian deployment. At first we tried deploying just an EJB JAR, basically the domain JAR, but this is was not sufficient. We need a way to also deploy the necessary 3rd party jars that we depend on. An EAR makes this easy as we can place then in the /lib directory.
For server/jar we changed things around by making a more clear distinction between unit tests and integration tests. We define an integration test class as one that extends AbstractEJB3Test. This means the testing is done against a running container. Unit test classes are those that do not have that requirement and basically run standalone, requiring just the JVM and any necessary classpath additions.
Server/jar test classes are now only unit tests.
Integration tests are now in server/itests-2. These are the Arquillian based tests. They run against a customized version of AS7, which gets unzipped under /target. Our test deployment is rhq.ear, so in fact the test environment is fairly close to reality. Rhq.ear is actually thinned, we remove the .war files, and then thickened, we add all of the test code and its dependencies.
Because we deploy the rhq.ear, you must rebuild the ear to pick up changes in the ejb classes (server jar, domain)!
When running the tests against Oracle it is necessary to supply -Pitest.oracle to get the proper datasource definition. Additionally, and this is not a new thing, you need -Pojdbc-driver to ensure you get the oracle driver in place. That driver must be available in a configured repo, it is not freely available.
Significant Changes
There are some significant changes brought about by the Arquillian-TestNG integration. The following are things to know and guidelines for writing or updating tests.
Arquillian (1.0.2) runs the TestNG lifecycle for every test!
This is critical to understand. It means that BeforeXXX/AfterXXX methods are all called for every test. Effectively this means that the only methods you can effectively use are BeforeMethod and AfterMethod.
Do not use BeforeClass/AfterClass or other flavors.
If you absolutely must you may be able to use "stand-ins" for these by using @Test method instead. By using dependencies, or priority, you can sort of simulate BeforeClass and AfterClass. But note that test priority is evaluated only in the scope of the group its test is in. Also realize that BeforeClass and AfterClass often cause unexpected issues due to test class interactions, and stand-ins can present the same problems. Any startup/teardown actions must be impervious to random test orderings amongst all classes.
Do not share data with instance variables.
The test class is effectively new'd for each test. This can make life difficult. Even though dependencies in test order execution are honored, it does not mean that you can set foo in testA and use the value of foo in testB. You have a few alternatives:
-
Just write a single, big test, that spans all of the tests you were thinking of splitting up and sharing data between.
This is probably the easiest route. In this case I suggest writing the tests separately, as private methods, as if they were real tests. And then creating a single consolidatedTest that calls them each in order. For example, see DriftTemplateManagerBeanTest.consolidatedNegativeUpdatesTest().
Know that BeforeMethod and AfterMethod are actually invoked twice per test.
Arquillian has two modes of testing, client and in-container. Client testing is such that the tests are basically remote clients and hit the deployed server in that fashion. We use the other mode, which is to run the tests in the container, meaning we can inject InitialContext, EntityManager, etc.. into the tests. Due to architectural integration issues, Arquillian calls BeforeMethod twice, once for each mode. We need to execute our code only for the in-container invocation.
The preferred mechanism for a BeforeMethod or AfterMethod is now to extend the base class methods offered in AbstractEJB3Test. So:
// NOT recommended
@BeforeMethod
public void myBeforeMethodName();
// Recommended
// By overriding the method you'll get built in in-container invocation only, as well as standard error handling. Note that there are a few flavors
// of the method, some taking parameters.
// IMPORTANT: use protected, not public. Public methods are considered test methods by testng if the class is annotated with @Test.
@Override
protected void beforeMethod();
// If you must use the annotation ensure you protect the invocation
@BeforeMethod( some attributes you just have to have )
public void myBeforeMethodName() {
if ( !inContainer()) {
return;
}
...
}
Handling test resource files
Remember, the tests are no longer running with a current working directory of the maven module directory. The tests are being deployed to and run inside the AS7 server. This impacts the way test resources are handled, and also how to interact with the file system.
-
Add test resources to git repo AND the deployment in AbstractEJB3Test
The itests-2/src/test/resources directory has all of the static files we use with the tests. These files must now be included in our rhq.ear test deployment. To do that, you must add it to the deployment that is built up in AbstractEJB3Test. Look in there for examples.
I took this opportunity to determine which resource files we are actually using. itests-2/src/test/resources/obsolete contains all of the legacy files that have yet to be proven to be needed. At some point these will be removed. Also, once we are happy with the set of remaining files, we may be able to simplify the deployment code in AbstractEJB3Test to just include all files under itests-2/src/test/resources, as opposed to explicitly adding each one, as it is the current mechanism. At that point just adding new files under itests-2/src/test/resources would be sufficient.
Know that we use an extension for running several test classes against a single deployment
By default Arquillian with testng will:
-
Start container
-
Deploy test deployment
-
Undeploy test deployment
-
Shutdown container
Although good for test exclusivity, it is prohibitively slow for a project with many test classes that doesn't need that level of test isolation. Working with the Arquillian developers we've put in place an Arquillian extension that allows us a single container start and deploy. This is in class SuiteDeploymentArquillianExtension.java which is based off of https://gist.github.com/3975179.
It should be noted that this extension uses Arquillian's (not testng) BeforeClass/AfterClass hooks, so those become unavailable for other use, but that is not an issue for us as we don't have any other extensions and observing those (or any other) events.
Notes
The lifecycle behavior may change with the next major version, but it will be like this for the foreseeable future.